#include "CVRender.h"
#include <SDL2/SDL_syswm.h>
#include <SDL2/SDL_video.h>
#include <stdio.h>


CVRender::CVRender() {
  wnd_ = nullptr;
  renderer_ = nullptr;
  screen_ = nullptr;
  renderer_ = nullptr;
  texture_ = nullptr;
  SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
  disp_mode_ = DisMode_FitWindow_KeepRatio;
  disp_mode_changed_ = false;
  rect_wnd_.x = 0;
  rect_wnd_.y = 0;
  rect_wnd_.w = 1280;
  rect_wnd_.h = 720;
  rect_disp_.x = 0;
  rect_disp_.y = 0;
  rect_disp_.w = 1280;
  rect_disp_.h = 720;
  image_w_ = 1280;
  image_h_ = 720;
}

CVRender::~CVRender() { SDL_Quit(); }

int CVRender::CreateWnd(HWND hWnd) {
  bool isWndValid = hWnd ? IsWindow(hWnd) : false;
  if (isWndValid) {
    screen_ = SDL_CreateWindowFrom(hWnd);
    wnd_ = hWnd;
  } else {
    screen_ = SDL_CreateWindow(
        nullptr, rect_wnd_.x, rect_wnd_.y, rect_wnd_.w, rect_wnd_.h,
        SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE);
  }
  if (!screen_) {
    return -1;
  }
  SDL_GetWindowSize(screen_, &rect_wnd_.w, &rect_wnd_.h);
  RebuildRender(image_w_, image_h_);
  SDL_SysWMinfo wmInfo = {0};
  SDL_VERSION(&wmInfo.version); // Initialize wmInfo
  SDL_GetWindowWMInfo(screen_, &wmInfo);
  return 0;
}

int CVRender::DestroyWnd() {
  if (texture_) {
    SDL_DestroyTexture(texture_);
    texture_ = nullptr;
  }

  if (renderer_) {
    SDL_DestroyRenderer(renderer_);
    renderer_ = nullptr;
  }

  if (screen_) {
    SDL_DestroyWindow(screen_);
    screen_ = nullptr;
  }
  wnd_ = nullptr;
  return 0;
}

int CVRender::SetDisplayMode(RenderDisMode_e mode, RenderRect_t *pRect,
                             int img_w, int img_h) {
  if (mode != disp_mode_) {
    disp_mode_changed_ = true;
  }

  if (pRect) {
    if ((pRect->x != rect_wnd_.x) || (pRect->y != rect_wnd_.y) ||
        (pRect->width != rect_wnd_.w) || (pRect->height != rect_wnd_.h)) {
      disp_mode_changed_ = true;
    }
  }

  if (disp_mode_changed_) {
    disp_mode_ = mode;
    if (disp_mode_ == DisMode_Free) {
      if (pRect) {
        rect_disp_.x = pRect->x;
        rect_disp_.y = pRect->y;
        rect_disp_.w = pRect->width;
        rect_disp_.h = pRect->height;
      }
    }
  }
  if ((img_w != image_w_) || (img_h != image_h_)) {
    RebuildRender(img_w, img_h);
  }
  GenerateDisplayRect(img_w, img_h);
  return 0;
}

int CVRender::CheckWndSize(int w, int h) {
  SDL_Rect rc_new = {0};
  SDL_GetWindowSize(screen_, &rc_new.w, &rc_new.h);
  if ((rc_new.w != rect_wnd_.w) || (rc_new.h != rect_wnd_.h)) {
    RebuildRender(w, h);
    rect_wnd_.w = rc_new.w;
    rect_wnd_.h = rc_new.h;
  }
  return 0;
}

int CVRender::RebuildRender(int width, int height) {
  if (texture_) {
    SDL_DestroyTexture(texture_);
    texture_ = nullptr;
  }
  if (renderer_) {
    SDL_DestroyRenderer(renderer_);
    renderer_ = nullptr;
  }
  renderer_ = SDL_CreateRenderer(screen_, -1, 0);
  texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_IYUV,
                               SDL_TEXTUREACCESS_STREAMING, width, height);
  image_w_ = width;
  image_h_ = height;
  return 0;
}

int CVRender::GenerateDisplayRect(int width, int height) {
  SDL_Rect rcDst = {0};
  SDL_Rect rcWnd = {0};
  rcWnd.x = 0;
  rcWnd.y = 0;
  SDL_GetWindowSize(screen_, &rcWnd.w, &rcWnd.h);
  if (disp_mode_ == DisMode_FullScreen) {
    SDL_SetWindowFullscreen(screen_, SDL_WINDOW_FULLSCREEN);
  } else if (disp_mode_ == DisMode_FitWindow_KeepRatio) {
    if (width * rcWnd.h / height > rcWnd.w) {
      rect_disp_.w = rcWnd.w;
      rect_disp_.h = rcWnd.w * height / width;
      rect_disp_.x = 0;
      rect_disp_.y = (rcWnd.h - rect_disp_.h) / 2;
    } else {
      rect_disp_.w = rcWnd.h * width / height;
      rect_disp_.h = rcWnd.h;
      rect_disp_.x = (rcWnd.w - rect_disp_.w) / 2;
      rect_disp_.y = 0;
    }
  } else if (disp_mode_ == DisMode_FitWindow_Stretch) {
    rect_disp_.x = rcWnd.x;
    rect_disp_.y = rcWnd.y;
    rect_disp_.w = rcWnd.w;
    rect_disp_.h = rcWnd.h;
  }
  return 0;
}

int CVRender::Update(unsigned char *pData[3], int size, int width, int height,
                     int pitch[3], RenderFormat_e fmt) {
  if ((width == 0) || (height == 0)) {
    return -1;
  }
  if ((!texture_) || (width != image_w_) || (height != image_h_)) {
    RebuildRender(width, height);
  }
  CheckWndSize(width, height);
  SDL_UpdateYUVTexture(texture_, NULL, pData[0], pitch[0], pData[1], pitch[1],
                       pData[2], pitch[2]);
  SDL_SetRenderDrawColor(renderer_, 240, 240, 240, SDL_ALPHA_OPAQUE);
  SDL_RenderClear(renderer_);
  GenerateDisplayRect(width, height);
  SDL_RenderCopy(renderer_, texture_, NULL, &rect_disp_);
  SDL_RenderPresent(renderer_);
  return 0;
}

int CVRender::GetDisplayRect(RenderRect_t *rc) {
  uint32_t fmt = 0;
  GenerateDisplayRect(image_w_, image_h_);
  rc->x = rect_disp_.x;
  rc->y = rect_disp_.y;
  rc->width = rect_disp_.w;
  rc->height = rect_disp_.h;
  return 0;
}
